﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LuaInterface;
using ElementumForever.Factory;
using ElementumForever.Utility;
using Microsoft.Xna.Framework;
using ElementumForever.Interfaces;
using ElementumForever.CommandPattern;
using System.Reflection;

namespace ElementumForever.Editer
{
    public class LuaSceneBuilder : IsceneBuilder
    {
        Lua lua;
        protected CommandManager _commandManager;
        private Dictionary<string, BaseEntity> products;


        public LuaSceneBuilder(string luaFilePath): this()
        {
            ReadScriptFile(luaFilePath);
        }

        public void Initialize()
        {
            try
            {
                lua = new Lua();// 生成 Lua 物件
            }
            catch (Exception ex)
            {
                Console.WriteLine("Lua 物件生成失敗:" + ex.Message);
                return;
            }
            products = new Dictionary<string, BaseEntity>();
        }

        public LuaTable SceneDefine
        {
            get
            {
                return GetTable(LuaConstants.SceneTableName);
            }
        }

        public LuaSceneBuilder()
        {
            LuaFactory.Initialize();
            _commandManager = new CommandManager(this);
            Initialize();
        }

        private Lua ReadScriptFile(string scriptname)
        {
            // 載入 Lua Script 
            try
            {
                lua.DoFile(scriptname);
                return lua;
            }
            catch (Exception ex)
            {
                Console.WriteLine("載入失敗:" + ex.Message);
                return null;
            }
        }

        public LuaTable GetTable(string fullPath)
        {
            return lua.GetTable(fullPath);
        }

        public CommandManager CManager { get { return _commandManager; } }

        public void InitializeEntity(BaseEntity entity, LuaTable entityDefine)
        {
            if (entityDefine != null)
            {
                entity.Name = entityDefine[LuaConstants.Name].ToString();//
                entity.Rotation = Convert.ToSingle(entityDefine[LuaConstants.Rotation]);//
                entity.Width = Convert.ToSingle(entityDefine[LuaConstants.Width]);//
                entity.Height = Convert.ToSingle(entityDefine[LuaConstants.Height]);//
                entity.Color = UnitHelper.ToColor((LuaTable)entityDefine[LuaConstants.Color]);
                BuildChildren(entity, entityDefine);
            }
        }

        public void BuildChildren(BaseEntity parent, LuaTable parentTable)
        {
            if (parentTable != null)
            {
                LuaTable children = (LuaTable)parentTable[LuaConstants.Children];
                foreach (LuaTable childTable in children.Values)
                {
                    BaseEntity child = LuaFactory.CreatObject(childTable);
                    child.Build(this, childTable);
                    string childId = AddProduct(child);
                   // AddEntity(childId, child);                    
                    AttachChildById(childId, parent);
                }
            }
        }

        public void InitializeScene(BaseScene scene, LuaTable sceneDefine)
        {
            if (sceneDefine != null)
            {
                InitializeEntity(scene, sceneDefine);
                scene.Zoom = Convert.ToSingle(sceneDefine[LuaConstants.Zoom]);
                scene.Center = new Vector2(Convert.ToSingle(sceneDefine[LuaConstants.CenterX]), Convert.ToSingle(sceneDefine[LuaConstants.CenterY]));
                scene.Color = UnitHelper.ToColor((LuaTable)((LuaTable)sceneDefine[LuaConstants.BackgroundProperties])[LuaConstants.Color]);
                scene.HasPhysics = Convert.ToBoolean(sceneDefine[LuaConstants.HasPhysics]);

                if (scene.HasPhysics)
                {
                    LuaTable properties = (LuaTable)sceneDefine[LuaConstants.PhysicalProperties];
                    Vector2 gravity = new Vector2(Convert.ToSingle(properties[LuaConstants.GravityX]), Convert.ToSingle(properties[LuaConstants.GravityY]));
                    bool doSleep = Convert.ToBoolean(properties[LuaConstants.DoSleep]);
                    scene.CreatPhysicalWorld(gravity, doSleep);
                }
            }
        }

        public void AttachChildById(String childId, BaseEntity parent)
        {
            if (GetProductByID(childId) != null)
                parent.AttachChild(GetProductByID(childId));
        }

        public void DetachChildById(String childId, BaseEntity parent)
        {
            if (GetProductByID(childId) != null)
                parent.DetachChild(GetProductByID(childId));
        }

        private void AddEntity(string id, BaseEntity entity)
        {
            if (ContainId(id))
                throw new Exception("id:" + id + "  already exists");
            this.products.Add(id, entity);
        }

        public bool RemoveProduct(string id)
        {
            return products.Remove(id);
        }

        public void ChangeId(string oldId, string newId)
        {
            if (!ContainId(oldId))
                throw new Exception("no such id:" + oldId);
            if (ContainId(newId))
                throw new Exception("id:" + newId + " already exists");

            BaseEntity _object = products[oldId];
            products.Remove(oldId);

            _object.Name = newId;
            products.Add(newId, _object);
        }

        public BaseEntity GetProductByID(string id) { return products[id]; }

        public bool ContainId(string id) { return products.ContainsKey(id); }

        public string AddProduct(BaseEntity entity)
        {
            string id = GenerateId(entity);
            AddEntity(id, entity);
            return id;
        }

        public string GetIDByProduct(BaseEntity entity)
        {
            foreach (var pair in products)
            {
                if (pair.Value == entity)
                    return pair.Key;
            }
            throw new Exception("cant find key");
        }

        public string GenerateId(BaseEntity entity)
        {
            return entity.Name;
        }
    }
}
